﻿/*
 * This file is part of MonoStrategy.
 *
 * Copyright (C) 2010-2011 Christoph Husse
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Affero General Public License as
 *  published by the Free Software Foundation, either version 3 of the
 *  License, or (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Affero General Public License for more details.
 *
 *  You should have received a copy of the GNU Affero General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * Authors: 
 *      # Christoph Husse
 * 
 * Also checkout our homepage: http://monostrategy.codeplex.com/
 */
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace MonoStrategy.RenderSystem
{
    public class MovableOrientedAnimation : RenderableAnimationClass
    {
        static private readonly Object m_ConstructorLock = new object();
        static private readonly SortedDictionary<String, DirectionCache> m_DirToAnim = new SortedDictionary<string, DirectionCache>();

        private class DirectionCache
        {
            public AnimationSet[] DirToAnim;
            public AnimationSet[] DirToFrozen;
        }

        private Direction m_Direction;
        private Direction m_PlayingDirection;
        private Boolean m_IsMoving;
        private Boolean m_HasJob;
        private DirectionCache m_DirectionCache;

        public event DChangeHandler<IPositionTracker, Direction> OnDirectionChanged;
        public Movable Movable { get; private set; }
        public Direction? DirectionOverride { get; set; }
        public Direction Direction
        {
            get { return m_Direction; }
            set
            {
                Direction old = m_Direction;

                m_Direction = value;

                if (OnDirectionChanged != null)
                    OnDirectionChanged(this, old, value);
            }
        }

        public MovableOrientedAnimation(TerrainRenderer inParent, AnimationClass inAnimClass, Movable inMovable)
            : base(inParent, inAnimClass, inMovable.Position)
        {
            if (inMovable == null)
                throw new ArgumentNullException();

            Movable = inMovable;

            lock (m_ConstructorLock)
            {
                if (!m_DirToAnim.TryGetValue(inAnimClass.Name, out m_DirectionCache))
                {
                    m_DirectionCache = new DirectionCache()
                    {
                        DirToFrozen = new AnimationSet[(int)Direction.Count],
                        DirToAnim = new AnimationSet[(int)Direction.Count],
                    };

                    if (inAnimClass.Sets.Count(e => e.Name == "Default") > 0)
                    {
                        var animSet = inAnimClass.FindSet("Default");

                        for (int i = 0; i < m_DirectionCache.DirToAnim.Length; i++)
                        {
                            m_DirectionCache.DirToAnim[i] = animSet;
                        }
                    }
                    else
                    {
                        for (int i = 0; i < m_DirectionCache.DirToAnim.Length; i++)
                        {
                            Direction dir = (Direction)i;

                            m_DirectionCache.DirToAnim[i] = inAnimClass.FindSet("Angle" + dir);
                            m_DirectionCache.DirToFrozen[i] = inAnimClass.FindSet("FrozenAngle" + dir);
                        }
                    }

                    m_DirToAnim.Add(inAnimClass.Name, m_DirectionCache);
                }
            }

            m_PlayingDirection = m_Direction = Direction._225;
            PlayFrozen();
        }

        private void PlayFrozen()
        {
            m_IsMoving = false;
            m_HasJob = false;

            if (m_DirectionCache.DirToFrozen[(int)m_PlayingDirection] != null)
            {
                Play(m_DirectionCache.DirToFrozen[(int)m_PlayingDirection]);
            }
            else
            {
                Play(m_DirectionCache.DirToAnim[(int)m_PlayingDirection]);
                Pause();
            }
        }

        internal override void Render(RenderPass inPass)
        {
            if (inPass == RenderPass.Pass1_Shadow)
            {
                /*
                 * For performance reason this branch is based on the assumption that the first
                 * thing in the rendering pipeline is calculating the Z-Values... So for the
                 * additional two calls there is no need to do this caluclation again!
                 */

                if (Movable != null)
                {
                    Direction newDirection = Direction;
                    CyclePoint newPosition;
                    Double newZShift;

                    if (Movable.Job is DirectionalJob)
                        Movable = Movable;

                    Movable.InterpolateMovablePosition(ref newDirection, out newPosition, out newZShift);

                    if(newDirection != Direction)
                        Direction = newDirection;

                    ZShiftOverride = newZShift;
                    Position = newPosition;
                }
            }

            if ((Movable == null) || Movable.IsMoving || Movable.HasJob)
            {
                if ((DirectionOverride != null) && DirectionOverride.HasValue)
                {
                    Direction = DirectionOverride.Value;
                }

                if ((m_PlayingDirection != Direction) || !(m_IsMoving || m_HasJob))
                {
                    m_IsMoving = Movable.IsMoving;
                    m_HasJob = Movable.HasJob;
                    m_PlayingDirection = Direction;

                    Play(m_DirectionCache.DirToAnim[(int)m_PlayingDirection]);
                }
            }
            else
            {
                if (m_IsMoving || m_HasJob)
                {
                    PlayFrozen();
                }
            }

            base.Render(inPass);
        }

    }
}
